use scopeguard::defer;

use bc_base::{
    BlockHeight, BlockId, PublicKeyHashBytes,
    block::Block,
    constants::{ALICE_PUBLIC_KEY_HASH, BOB_PUBLIC_KEY_HASH, GENESIS_BLOCK_ID},
};

use tests::common;

async fn create_block_and_add_to_bc(
    test_env: &common::TestEnv,
    prev_block_hash: &BlockId,
    block_height: BlockHeight,
    miner_pubkey_hash: &PublicKeyHashBytes,
    block_timestamp: u32,
) -> (BlockId, Block) {
    common::local::create_block_only_contain_coinbase_and_add_to_blockchain_with_timestamp(
        test_env.bc_manager.clone(),
        prev_block_hash,
        block_height,
        miner_pubkey_hash,
        block_timestamp,
    )
    .await
}

#[tokio::test]
async fn reorganize_block() {
    let (test_env, tmp_dir) = common::setup("reorganize_block", false, None, false).await;
    defer! {
        tmp_dir.close().unwrap();
    };

    // do test here
    {
        let alice_pubkey_hash = PublicKeyHashBytes::from_hex_str(ALICE_PUBLIC_KEY_HASH).unwrap();
        let bob_pubkey_hash = PublicKeyHashBytes::from_hex_str(BOB_PUBLIC_KEY_HASH).unwrap();
        let sys_time_now = 1746757043; // bc_util::get_current_sys_time_seconds();
        let mut block_timestamp = sys_time_now as u32;
        let mut block_height: BlockHeight = 1;

        // create block 1
        let prev_block_hash = BlockId::from_hex_str(GENESIS_BLOCK_ID).unwrap();
        let (block_id_1, _) = create_block_and_add_to_bc(
            &test_env,
            &prev_block_hash,
            block_height,
            &alice_pubkey_hash,
            block_timestamp,
        )
        .await;
        block_timestamp += 1;
        tracing::debug!("block_id_1: {}", &block_id_1);

        let bc_manager = test_env.bc_manager.lock().await;
        assert_eq!(bc_manager.read_best_height().await, 1);
        assert_eq!(
            &bc_manager.read_latest_block_id_in_best_chain().await,
            &block_id_1
        );
        assert_eq!(bc_manager.block_number().await, 2);
        assert_eq!(bc_manager.transaction_number().await, 2);
        drop(bc_manager);

        // create block 2
        let prev_block_hash = &block_id_1;
        block_height += 1;
        let (block_id_2, _) = create_block_and_add_to_bc(
            &test_env,
            prev_block_hash,
            block_height,
            &alice_pubkey_hash,
            block_timestamp,
        )
        .await;
        block_timestamp += 1;
        tracing::debug!("block_id_2: {}", &block_id_2);

        let bc_manager = test_env.bc_manager.lock().await;
        assert_eq!(bc_manager.read_best_height().await, 2);
        assert_eq!(
            &bc_manager.read_latest_block_id_in_best_chain().await,
            &block_id_2
        );
        assert_eq!(bc_manager.block_number().await, 3);
        assert_eq!(bc_manager.transaction_number().await, 3);
        drop(bc_manager);

        // create block 2.1
        let prev_block_hash = &block_id_1;
        // block_height += 1;
        let (block_id_2_1, _) = create_block_and_add_to_bc(
            &test_env,
            prev_block_hash,
            block_height,
            &bob_pubkey_hash,
            block_timestamp,
        )
        .await;
        block_timestamp += 1;
        tracing::debug!("block_id_2_1: {}", &block_id_2_1);

        let bc_manager = test_env.bc_manager.lock().await;
        assert_eq!(bc_manager.read_best_height().await, 2);
        assert_eq!(
            &bc_manager.read_latest_block_id_in_best_chain().await,
            &block_id_2
        );
        assert_eq!(bc_manager.block_number().await, 4);
        assert_eq!(bc_manager.transaction_number().await, 4);
        // bc_manager.dump_all_txs().await;
        drop(bc_manager);

        // create block 3
        let prev_block_hash = &block_id_2_1;
        block_height += 1;
        let (block_id_3, _) = create_block_and_add_to_bc(
            &test_env,
            prev_block_hash,
            block_height,
            &bob_pubkey_hash,
            block_timestamp,
        )
        .await;
        // block_timestamp += 1;
        tracing::debug!("block_id_3: {}", &block_id_3);

        let bc_manager = test_env.bc_manager.lock().await;
        assert_eq!(bc_manager.read_best_height().await, 3);
        assert_eq!(
            &bc_manager.read_latest_block_id_in_best_chain().await,
            &block_id_3
        );
        assert_eq!(bc_manager.block_number().await, 4);
        // bc_manager.dump_all_block().await;
        // bc_manager.dump_all_txs().await;
        assert_eq!(bc_manager.transaction_number().await, 4);
        drop(bc_manager);
    }

    common::teardown(test_env).await;
}
